#dotenv nodejs
Explore tagged Tumblr posts
Text
Access Environment Variable in Nodejs JavaScript Application | Reading ENV Variable Example
Full Video Link https://youtu.be/dxrNopL1sbQ Hello friends, new #video on #reading #accessing #environmentvariables in #nodejs #projeect #application #tutorial #examples is published on #codeonedigest #youtube channel. @java #java #aws #a
In this video, we will read the environment variable in nodejs javascript project. We will learn what “dotenv” module in nodejs javascript. How to use “dotenv” package in our nodejs javascript project. ** Important Nodejs Javascript Packages or Modules ** Dotenv – DotEnv is a lightweight npm package that automatically loads environment variables from a .env file into the process.env object. To…
View On WordPress
#dotenv#dotenv example#dotenv in node js#dotenv module#dotenv module in node js#dotenv module not found#dotenv nodejs#dotenv package#dotenv package install#dotenv package nodejs#dotenv package.json#dotenv tutorial#dotenv tutorial nodejs#environment variable#environment variables#javascript environment variable#linux environment variables#node js javascript#node js javascript tutorial#nodejs#python environment variables#set environment variables
0 notes
Text
Flexpeak - Front e Back - Opções
Módulo 1 - Revisão de JavaScript e Fundamentos do Backend: • Revisão de JavaScript: Fundamentos • Variáveis e Tipos de Dados (let, const, var) • Estruturas de Controle (if, switch, for, while) • Funções (function, arrow functions, callbacks) • Manipulação de Arrays e Objetos (map, filter, reduce) • Introdução a Promises e Async/Await • Revisão de JavaScript: Programação Assíncrona e Módulos • Promises e Async/Await na prática Módulo 2 – Controle de Versão com Git / GitHub • O que é controle de versão e por que usá-lo? • Diferença entre Git (local) e GitHub (remoto) • Instalação e configuração inicial (git config) • Repositório e inicialização (git init) • Staging e commits (git add, git commit) • Histórico de commits (git log) • Atualização do repositório (git pull, git push) • Clonagem de repositório (git clone) • Criando um repositório no GitHub e conectando ao repositório local • Adicionando e confirmando mudanças (git commit -m "mensagem") • Enviando código para o repositório remoto (git push origin main) • O que são commits semânticos e por que usá-los? • Estrutura de um commit semântico: • Tipos comuns de commits semânticos(feat, fix, docs, style, refactor, test, chore) • Criando e alternando entre branches (git branch, git checkout -b) • Trabalhando com múltiplos branches • Fazendo merges entre branches (git merge) • Resolução de conflitos • Criando um Pull Request no GitHub Módulo 3 – Desenvolvimento Backend com NodeJS • O que é o Node.js e por que usá-lo? • Módulos do Node.js (require, import/export) • Uso do npm e package.json • Ambiente e Configuração com dotenv • Criando um servidor com Express.js • Uso de Middleware e Rotas • Testando endpoints com Insomnia • O que é um ORM e por que usar Sequelize? • Configuração do Sequelize (sequelize-cli) • Criando conexões com MySQL • Criando Models, Migrations e Seeds • Operações CRUD (findAll, findByPk, create, update, destroy) • Validações no Sequelize • Estruturando Controllers e Services • Introdução à autenticação com JWT • Implementação de Login e Registro • Middleware de autenticação • Proteção de rotas • Upload de arquivos com multer • Validação de arquivos enviados • Tratamento de erros com express-async-errors Módulo 4 - Desenvolvimento Frontend com React.js • O que é React.js e como funciona? • Criando um projeto com Vite ou Create React App • Estruturação do Projeto: Organização de pastas e arquivos, convenções e padrões • Criando Componentes: Componentes reutilizáveis, estruturação de layouts e boas práticas • JSX e Componentes Funcionais • Props e Estado (useState) • Comunicação pai → filho e filho → pai • Uso de useEffect para chamadas de API • Manipulação de formulários com useState • Context API para Gerenciamento de Estado • Configuração do react-router-dom • Rotas Dinâmicas e Parâmetros • Consumo de API com fetch e axios • Exibindo dados da API Node.js no frontend • Autenticação no frontend com JWT • Armazenamento de tokens (localStorage, sessionStorage) • Hooks avançados: useContext, useReducer, useMemo • Implementação de logout e proteção de rotas
Módulo 5 - Implantação na AWS • O que é AWS e como ela pode ser usada? • Criando uma instância EC2 e configurando ambiente • Instalando Node.js, MySQL na AWS • Configuração de ambiente e variáveis no servidor • Deploy da API Node.js na AWS • Deploy do Frontend React na AWS • Configuração de permissões e CORS • Conectando o frontend ao backend na AWS • Otimização e dicas de performance
Matricular-se
0 notes
Text
Thử viết một command tool bằng NodeJS và publish lên npmjs.com
Intro
Các bạn có biết tại sao Node.js hay rộng hơn là Javascript nó phát triển mạnh mẽ và cộng đồng phát triển nó lớn vậy không?
Một trong những nguyên nhân là sự phát triển rất dễ dàng các thư viện, framework và publish lên cộng đồng, cụ thể chính là npm với trang npmjs.com
Hôm nay mình xin sẽ tận tay chứng minh tại sao nó lại dễ đến vậy với việc mình thử viết command tool (cli) **và **publish lên npmjs.com nó đơn giản như thế nào.
const path = require('path') const env = process.env.NODE_ENV === 'production' ? 'production.env' : '.env' require('dotenv').config({ path: path.join(__dirname, env) })
Xin thưa, mình để ảnh này vào cho đỡ chống
1. Ý tưởng
Mình đang có 1 task mình đang phải làm hàng ngày là review các node package và publish project đó lên npmjs.com. Công việc đó lặp đi lặp lại hàng ngày và mình thấy rất nhàm chán và tốn thời gian nên mình đã quyết định dành ra 1 buổi để ngồi viết 1 cái tool để gõ đúng 1 lệnh làm tất cả mọi thứ. Việc của mình chỉ là ung dung ngồi đợi nó làm xong việc. Easy!
2. Lấy yêu cầu
��ầu tiên mình sẽ liệt kê yêu cầu của command line tool sẽ phải làm gì?
Kiểm tra project là git folder và đang ở nhánh develop, nếu không đang ở nhánh develop phải chuyển về nhánh develop.
Kiểm tra project là một node package
Tăng version cho package đó và commit file vừa sửa đổi và push lên develop
Merge code từ develop vào master
Publish project đó lên npmjs.org (lưu ý là phải hỗ trợ cả private packages)
Quay trở lại nhánh develop ở trạng thái bình thường.
3. Triển khai
Đầu tiên mình cần đặt tên cho package của mình, mình kiểm tra trên npmjs.com xem tên package có available hay không và mình quyết định đặt tên package là: my-publisher và command line sau khi cài xong sẽ có lệnh là publish (mình không muốn đặt lệnh dài dòng nên để tên vậy).
Ok, đã có tên, mình bước ngay vào việc tạo 1 project node với lệnh:
npm init
Tiếp theo mình cần xử lý đến input đầu vào qua command line, và mình sử dụng meow vì nó khá dễ dùng và tiện lợi. Mình chỉ cần khai báo các options là có ngay 1 cli với việc xử lý input đầu vào đầy đủ.
Tiếp theo mình cần xử lý các script mình đã liệt kê ở phần 2 với việc chạy các lệnh và mình quyết định sử dụng execa để run các lệnh. Package này khá dễ dùng và hỗ trợ promise, tiện hơn rất nhiều nếu mình dùng **child_process** của Node core. Phần này đã tốn mất ít nhất 4 tiếng của mình để implement các lệnh này sang dạng code.
Ngoài ra mình có sử dụng thêm simple-git để xử lý các tác vụ liên quan đến git.
Ok! Cơ bản mình đã có phần core đầy đủ để biến project mình thành 1 command tool. Giờ mình cần biến nó thành 1 command tool thực thụ. Để biến 1 project node thành 1 command tool thì npm có 1 trường bin trong file package.json cho phép khai báo các command muốn cài đặt vào biến môi trường.
"bin": { "publish": "bin/index.js" }
Ví dụ như trong project này, mình đã tạo 1 file bin/index.js để khi chạy lệnh publish nó sẽ gọi đến file này.
Bước cuối cùng của mình sẽ cần publish package này lên npmjs.com. Việc này cực kì đơn giản, bạn chỉ đơn giản đăng ký 1 tài khoản trên npmjs.com. Rồi chạy lệnh npm login ở dưới máy. Sau đó quay trở lại project hiện tại và chạy lệnh:
npm publish --access public
và chờ nó publish lên npmjs.com
4. Thành quả
Sau khi đã publish lên npmjs.com có nghĩa là bạn đã chính thức vừa trở thành một contributor cho cộng đồng npmjs.com. Vỗ tay nào!
Việc đơn giản bây giờ là cài đặt package về máy với lệnh cực kỳ đơn giản:
npm i -g my-publisher
Sau đó trên máy có thể dùng ngay lệnh publish và ngồi rung đùi chờ mọi thứ tự động chạy. Easy!
Đây là thành quả mình đã thử với my-publisher của mình
Các bạn có thể tham khảo project của mình tại đây: https://github.com/tutv/my-publisher
4. Mở rộng
Thường việc chạy các command tool thường sẽ mất thời gian và cần phải hiện ra tình trạng đang chạy đến phần nào, có lỗi hay không. Để làm việc đó, mọi người đơn giản sẽ dùng console của NodeJS cung cấp để hiển thị ra mọi thông tin đang chạy đúng không? Nhưng ở đây mình đã tìm ra thêm 1 thứ khá hay ho là cách để tạo ra các lệnh và có spinner hay loading để hiện thị task đang chạy thay vì thô cứng với việc hiển thị text qua console. Và đó chính là nhờ **ora**, các bạn có thể xem demo nó như thế này:
Hiệu ứng loading với command line
5. Kết luận
Với các công cụ, thư viện có sẵn trên npmjs.com mình đã nhanh chóng hoàn thành xong 1 command tool **bằng NodeJS và publish nó lên **npmjs.com. Việc của mình chỉ cần tập trung vào việc viết phần chính của command tool. Các bạn có thể thử ngay và thỏa sức sáng tạo với ý tưởng của chính mình nhé.
Hãy để lại bình luận nếu các bạn đã viết được command tool nào của chính mình nhé.
1 note
·
View note
Photo

Managing Environment Variables in Node.js with dotenv ☞ http://bit.ly/2KuvCME #nodejs #javascript
3 notes
·
View notes
Text
Top Nodejs Security Practices
Since it bridges the gap between front-end and back-end applications, Node.js is widely used today in web and mobile applications. Unfortunately, the increased use of Node.js has given attackers new opportunities to exploit misconfigurations and vulnerabilities.
The developers have a huge responsibility to improve application security to prevent these attacks. As a result, in this article, I'll go over the 5 best practices for enhancing safety in your Node.js application.
Top 5 Nodejs Security Practices
Validate User Inputs Injection-based attacks have made it into the OWASP and SANS Top 25 CWE lists, and they can take many different forms. Validating all inputs before processing data is critical for avoiding these attacks. For instance, the phone number field should only accept a specific numeric and special character format.
Adding a layer of security with a Reverse Proxy A reverse proxy is a security layer that allows a server to access a web application. The server filters requests and ensures that it does not become overburdened. We can set a rate limit on the proxy configuration and implement connection timeouts.
Keeping track of application secrets We should use standard methods to store application secrets because they are essential. This information can be stored in operating system environment variables, and the dotenv package is the best way to manage secrets when multiple variables need to be instantiated.
Using Security Linters to Identify Code Vulnerabilities Before compiling, code linters assist developers in identifying various issues in the code. They can detect the most common problems and force programmers to adhere to best practices. These code linters have their own rules that developers can tweak to fit their needs. As a result, developers must enable them in the linter configuration before using the security vulnerability detection rules.
Data leakage prevention You can't rely on what you get from the front end, and you shouldn't rely on the data you'll be sending. You can send all of the information for the specific object to the front end and filter what will appear there. It's easy to do because hackers always look for hidden data sent from the back-end. In this case, the best solution is only to consider sending the required information. If the first and last names are required, they are only retrieved from the database. It is common to do extra work, but it is well worth the effort.
Conclusion
We looked at the best practices for Node.js security in 2021 and the years ahead. It is critical to secure and precisely safeguard web applications. On the other hand, people do not implement security measures regularly and forget to do so when there are tight project deadlines.
Consider Node.js Security solutions at each stage of the software development life cycle to ensure there are no additional security risks from conception to production.
0 notes
Text
How to Build Microservices with Node.js
Divide into independent chunks to build the entire application.
Introduction
In the last blog, we learned about Microservice Architecture and Why Build Microservices with Node.js. If you haven’t explored or read much about Microservice Architecture then I would recommend visiting our last blog, to have basic theoretical knowledge. The blog deals with What, Why, and When with Microservices. We will cover the remaining question ‘How’ in this tutorial.
You are exactly right! In this tutorial, we will learn how to build a basic demo application for illustrating Microservices with Node.js. The tutorial will consist of basic and simple steps that you can follow with us and start building your own demo app.
Without further ado, let’s get started with our application.
Microservices with Node.js Example
In this demo application, we will build microservices with NodeJS which will connect to an external API.
The app will have three services: Book, Customer, and Order services. Each of the services will have individual servers running on different ports. These services will communicate with each other through REST APIs. We all know that if it wasn’t microservices architecture we would have only one server running. But, in microservices architecture the scenario is different.
So, this was about what we are building in this tutorial, let’s get started with the coding part.
Initial Set-Up
Make sure you have Node.js installed. Visit Nodejs.org to download the latest Node version, if your system doesn’t have Node.js.
Run npm init in the project root folder. This will create a package.json file that will create certain questions about the package, if you are not sure how to respond you can use the default.
We will use four packages, Express, mongoose, axios, and dotenv which can be installed as follows:
$ npm install express mongoose axios dotenv --save
Project Structure
Refer to the below image for the project structure. The structure can vary from person to person.
Read more: Creating Database Connection
0 notes
Link
This blog post is Part 2 of the blog post series on Building Real-Time TV Dashboards with Salesforce Data, Platform Events & a Raspberry Pi. Part 1 – The Intro Part 2 – Salesforce Setup Part 3 – Raspberry Pi Setup Let’s get started with the Raspberry Pi setup. The idea is to have the Dashboard running on the Pi. This can be done in various different ways but for the sake for this project we will be hosting and running the dashboard on the Raspberry Pi. We will be building the Dashboard using the LWC OSS (Lightning Web Components Open Source) Framework. Pro Tip (Optional but totally worth trying out): Setup SSH and VNC on the Pi for super easy remote development and remote control. Step 0: Install the OS (Skip this step if you already have the OS installed) I am using Raspbian OS on my Pi and I set it up using NOOBS – New Out Of the Box Software and it makes the installation very easy. Here is a comprehensive step by step guid on installing the OS using NOOBS – https://www.raspberrypi.org/help/noobs-setup/2/ Step 1: Disable the Screensaver This is an important step because by default the PI’s screen keeps blanking out every 15 mins. As we will be having a standalone realtime dashboard running on the PI, we need to have our screensaver disabled. Easiest way to get this done is by installing xscreensaver. In the Terminal, run the following command to install it. This might take a few minutes $ sudo apt-get install xscreensaver Once installed, go to Preferences option in the main desktop menu. You should find the screen saver application. Launch it and search for the option to disable it completely. Step 2: Install Node.js Run the following commands in the Terminal Update your system package list sudo apt-get update Upgrade all your installed packages to their latest version sudo apt-get dist-upgrade Download the latest version of Node.js curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - Install it sudo apt-get install -y nodejs To check if the installation was successful, run the following command and it should return the version number of Node.js node -v Step 3: Create LWC App The Dashboard Interface is an LWC App. So, the goal is to have an LWC App running on the Raspberry Pi and it is totally up to you on how and where you build the LWC app. As long as there is an LWC App to be run on the Pi, you can either develop it on the Pi or develop it somewhere else(like your local machine) and migrate it to the Pi. For example, I developed it on my local machine and pushed it to the Pi via version control(bitbucket) so that I could maintain a backup and also sync code changes between my system and the pi. Feel free choose which ever way you are comfortable with and make sure you have Node.js installed as it is required to develop and run LWC Apps. 3.1 Create tv-dashboard LWC App On the command line/terminal, run command: npx create-lwc-app tv-dashboard When prompted about the application details, fill in the details as following Package name for npm: Press Enter to accept the default Description: Type the app description or press Enter to accept the default Author: Type your name or press Enter to accept the default Version: Press Enter to accept the default License: Press Enter to accept the default Who is the GitHub owner of the repository ( https://github.com/OWNER/repo): Type in your GitHub owner name or leave it blank and press Enter What is the GitHub name of the repository ( https://github.com/owner/REPO): Type in your GitHub repo name or Press Enter to accept the default Select a package manager: Use your arrow keys to select npm press Enter Use TypeScript or JavaScript: Use your arrow keys to select JavaScript press Enter Use custom Express server configuration: Enter y to install the server and press Enter Once the application is created, you will see the following message: Created conference-app in /YOUR/FILE/STRUCTURE/tv-dashboard. Checkout the `scripts` section of your `package.json` to get started. 3.2 Install A Few Packages Open command line/terminal Navigate to the directory containing the tv-dashboard application cd tv-dashboard Run the following command: npm install jsforce dotenv socket.io chart.js @salesforce-ux/design-system But, what are these applications and why do we need them? jsforce – JavaScript library to interact with Salesforce Orgs and APIs dotenv – To store our Salesforce credentials as environment variables in a .env file and reference them in or app socket.io – JavaScript library that enables real-time bidirectional event-based communication. We are using this to communicate our platform events from the LWC Server to LWC Frontend(charts). Works in a pub-sub model. chart.js – JavaScript library to create simple and beautiful HTML5 charts using canvas elements @salesforce-ux/design-system – Salesforce Lightning Design System (SLDS) – CSS framework to make our app beautiful 3.3 Create the .env file and declare our salesforce credentials as environment variables Open the tv-dashboard application folder Create a file with .env (with a leading period) as file name. Make sure that you are creating this folder in the root folder i.e., tv-dashboard Add the following the content into the .env file .env SF_LOGIN_URL=https://login.salesforce.com SF_USERNAME=YOUR_USERNAME SF_PASSWORD=YOUR_PASSWORD SF_TOKEN=YOUR_SECURITY_TOKEN Make sure to update the above placeholder values with your data SF_USERNAME: Your Salesforce Org’s username. SF_PASSWORD: Your Salesforce Org’s password. SF_TOKEN: Your Salesforce Org’s security token. 3.4 Update lwc-services.config.js to use the Lightning Design System SLDS In the tv-dashboard application folder, open the scr folder Open the file lwc-services.config.js Add the following in the resources section of your config { from: 'node_modules/@salesforce-ux/design-system/assets', to: 'dist/resources/assets' } 3.5 Add LWC Express Server Code In the tv-dashboard application folder, open folder server Open the file index.js Add the following code at the beginning of the file (before the line module.exports) src/server/index.js console.log(`Running server on port ${PORT}`)); // Start backend server server.listen(PORT, openDashboard); function openDashboard() { console.log(`Running socket server on port ${PORT}`); if (npm_lifecycle_event === 'serve') { console.log('Launching Dashboard!!'); exec( 'chromium-browser --noerrdialogs --kiosk http://0.0.0.0:3002 --incognito --disable-translate' ); } } 3.6 EDIT Client INDEX.html to add SLDS Stylesheet In the tv-dashboard application folder, open folder client Open the file index.html Replace the file content with the following code /src/client/index.html 3.7 Edit Client INDEX.js to use Synthetic Shadow DOM for the app and components to be able to use SLDS In the tv-dashboard application folder, open folder client Open the file index.js Add the following code at the beginning of the file content import '@lwc/synthetic-shadow'; 3.8 Create LWC Chart Component for visualising Opportunity Count By their Stage In the tv-dashboard application folder, open the folder my Create a folder named opportunitiesByStage Inside opportunitiesByStage folder, create a file opportunitiesByStage.html with the following content /src/client/modules/my/opportunitiesByStage.html Inside opportunitiesByStage folder, create a file opportunitiesByStage.js with the following content /src/client/modules/my/opportunitiesByStage.js import { LightningElement, api, track } from 'lwc'; export default class opportunitiesByStage extends LightningElement { @api sobject = ''; @api socket; @track socketInitialized = false; @track chartInitialized = false; chart; //object to keep track of the number of opportunities per stage chartData = {}; chartConfig = { type: 'doughnut', data: { datasets: [ { data: [], backgroundColor: [ '#3296ED', '#9D53F2', '#E287B2', '#26ABA4', '#77B9F2', '#C398F5', '#4ED4CD' ] } ], labels: [] }, options: { responsive: true, elements: { arc: { borderWidth: 0 } }, legend: { position: 'right', labels: { usePointStyle: true } }, animation: { animateScale: true, animateRotate: true } } }; async renderedCallback() { if (!this.socketInitialized && this.socket) { this.initializeSocket(); } if (!this.chartInitialized && this.socketInitialized) { await this.initializeChart(); } } initializeSocket() { //bind the onSocketEvent method to the 'cdc' socket event to update the chart with new incoming data this.socket.on('cdc', this.onSocketEvent.bind(this)); this.socketInitialized = true; } //initialize chart with chart.js async initializeChart() { await require('chart.js'); const ctx = this.template .querySelector('canvas.chart') .getContext('2d'); this.chart = new window.Chart(ctx, this.chartConfig); this.chartInitialized = true; } onSocketEvent(data) { const { changeType, entityName } = data.ChangeEventHeader; // check to make sure the change event is for the configured sobject and the record event is CREATE if ( this.sobject.toLowerCase() !== entityName.toLowerCase() || changeType !== 'CREATE' ) { return; } //update the chartData to increment the corresponding opportunity stage counter this.chartData[data.StageName] = this.chartData[data.StageName] + 1 || 1; //sort chartData in descending order let sortable = Object.entries(this.chartData); sortable.sort(function(a, b) { return b[1] - a[1]; }); //update chartData with sorted data this.chartData = Object.fromEntries(sortable); //add the updated data to the chart object this.chart.data.labels = Object.keys(this.chartData); this.chart.data.datasets[0].data = Object.values(this.chartData); //update the chart to reflect latest data this.chart.update(); } } 3.9 Create an LWC Component to view Chatter Announcements In the tv-dashboard application folder, open the folder my Create a folder named chatterAnnouncement Inside chatterAnnouncement folder, create a file chatterAnnouncement.html with the following content /src/client/modules/my/chatterAnnouncement.html Inside chatterAnnouncement folder, create a file chatterAnnouncement.html with the following content /src/client/modules/my/chatterAnnouncement.js 12 ? hour - 12 : hour; hour = this.formatNumber(hour); min = this.formatNumber(min); sec = this.formatNumber(sec); this.time = `${hour}:${min} ${ap}`; const months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; const days = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]; const curWeekDay = days[today.getDay()]; const curDay = today.getDate(); const curMonth = months[today.getMonth()]; const curYear = today.getFullYear(); this.date = `${curWeekDay}, ${curDay} ${curMonth}, ${curYear}`; this.dateTime = `${this.date} • ${this.time}`; } formatNumber(num) { return num < 10 ? '0' + num : num; } } 3.10 Edit the App Component to add the Chart and the Chatter Announcements Components and also subscribe to our Socket.io server In the tv-dashboard application folder, open the folder app Open the file app.html and replace its content with the following /src/client/modules/my/app.html Open the file app.js and replace its content with the following code to initialise and subscribe to the backend socket server /src/client/modules/my/app.js { console.log('socket connected!'); this.socketReady = true; }); } async closeSocket() { this.socket.close(); this.socket = null; } } Step 4: Run The Dashboard We run the dashboard on chromium in kiosk mode, this helps show the in dashboard full screen. You can run the dashboard using the following command npm run build && npm run serve If you are developing, making code changes and would like for them to reflecting in real time, use the command npm run watch Note: Only the npm run serve command launches the Dashboard automatically. When using the npm run watch command, you would need manually view the app in the browser. Once you run the dashboard, start creating some Opportunities and Chatter Announcements in your org and you will see the Dashboard update in real-time! Step 5 (Optional): Auto Launch the Dashboard on Raspberry Pi Startup 5.1 Change the LWC Dashboard App Folder Permissions This wouldn’t be necessary if you are created everything on the pi. But when I used Git to sync code, I came across a the EACCESS Permission error and this following terminal command took care of the error. sudo chown -R pi:pi ABSOLUTE_PATH_TO_LWC_APP_FOLDER Make sure to replace the ABSOLUTE_PATH_TO_LWC_APP_FOLDER with the actual absolute path of your Dashboard LWC App folder. To get the absolute path, right click your Dashboard LWC App folder and Select Copy Path(s) 5.2 Create a executable shell script file Open the tv-dashboard folder Create a file with name run.sh and add the below as its content run.sh #!/bin/bash cd ABSOLUTE_PATH_TO_LWC_APP_FOLDER npm run build && npm run serve Make sure to replace the ABSOLUTE_PATH_TO_LWC_APP_FOLDER with the actual absolute path of your Dashboard LWC App folder. To get the absolute path, right click your Dashboard LWC App and Select Copy Path(s) 5.3 Edit the Raspberry Pi Autostart Script Open Terminal Run the following command sudo nano /etc/xdg/lxsession/LXDE-pi/autostart Add the following line to the file @lxterminal --command "ABSOLUTE_PATH_TO_SHELL_SCRIPT_FILE" Make sure to replace the ABSOLUTE_PATH_TO_SHELL_SCRIPT_FILE with the actual absolute path of the shell script created(in the previous step) inside the Dashboard LWC App folder. To get the absolute path, right click the shell script file that you created(in the pervious step) inside the Dashboard LWC App and Select Copy Path(s) Hit Control+S to Save Then Control+X to Exit the editor And… that is it! We learnt how to go about Build Real-Time TV Dashboards with Salesforce Data, Platform Events & a Raspberry Pi. We’ve scratched the surface with just 2 components and here is the tv-dashforce GitHub Project that not only contains the components created in this blog post but also different chart components along with Twitter Live Stream, Clock, Weather and Holidays. Make sure to check it out! GitHub Project- https://github.com/Minerva18/tv-dashforce This can also be done in another way where, we can host the Dashboard LWC App on the cloud like Heroku/AWS/Google etc. and use the URL to show the Dashboard LWC app via chromium kiosk mode on the Raspberry Pi. Raspberry Pi Resources https://www.raspberrypi.org/ What is a Raspberry Pi? https://www.raspberrypi.org/help/what-%20is-a-raspberry-pi/ Raspberry Pi Documentation and Setup Guides https://www.raspberrypi.org/documentation/ Installing OS https://www.raspberrypi.org/documentation/installation/noobs.md SSH Setup Guide https://www.raspberrypi.org/documentation/remote-access/ssh/ VNC Setup Guide https://www.raspberrypi.org/documentation/remote-access/vnc/ Screensaver https://www.raspberrypi.org/documentation/configuration/screensaver.md Securing your Raspberry Pi https://www.raspberrypi.org/documentation/configuration/security.md LED warning flash codes https://www.raspberrypi.org/documentation/configuration/led_blink_warnings.md The post Build Real-Time TV Dashboards with Salesforce Data, Platform Events & a Raspberry Pi – Part 3 – Raspberry Pi Setup appeared first on Minerva18.
0 notes
Text
VSCodeでコードを書く時に役立つ設定・テーマ・機能拡張、知っておくと便利なテクニック
Visual Studio CodeでWeb制作・開発のコードを書く時に役立つ設定・テーマ・機能拡張、知っておくと便利なテクニックを紹介します。
My Web Development VS Code Settings, theme, Extensions, tips and tricks br lampewebdev(@lampewebdev)
下記は各ポイントを意訳したものです。 ※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
私がVSCodeを使用している理由
私はプログラミングを始めて以来、たくさんのエディタやIDEを使ってきました。Eclipse、Netbeans、Notepade ++、Brackets Editor、Sublime Text 2、Sublime Text 3、Webstorm、Atom Editor、他にもあると思いますが、もう忘れました。
私は現在、Visual Studio Code(VSCode)を使用しています。VSCodeはGoogleトレンドによると、最も検索されたエディタです。その理由はいくつか考えられます。
VSCodeは速く動作するように感じます。 もしVSCodeが遅く感じるのであれば、それはVSCodeのせいではなく、不十分に実装された機能拡張が原因です。
ファイル検索も非常に高速で、あっという間です。待ち時間はありません。
軽量ですが、必要な機能があらかじめ揃っています。ファイル検索、デバッグ、���本的なgit GUIなど。
テーマやカスタマイズのサポート、VSCodeのすべてを自分用に変更できます。
便利な機能拡張もたくさん用意されています。
賞賛の言葉はこれくらいにして、VSCodeを私が実際にどのように使用しているか紹介します。
VSCodeのテーマ・アイコン・フォント
私は毎日数時間、VSCodeを使用するので、テーマやアイコンやフォントは使いやすいものにしています。
VSCodeのテーマ
SynthWave '84
glowは有効にせず、カスタムCSSも追加していません。
ファイルのアイコン
Simple icons
Simple Iconsは見やすいカラーで、ミニマルなアイコンテーマです。必要なファイルを素早く見つけることができます。
フォント
Hasklig
コードが見やすいフォントで、合字もサポートしています。 フォントの設定は下記の通りです、この構成の設定方法については後述します。
"editor.fontFamily": "Hasklig, Consolas, 'Courier New', monospace",
"editor.fontLigatures": true,
"editor.fontSize": 15,
VSCodeの機能拡張
Bracket Pair Colorizer 2
この機能拡張は、すべてのエディタに組み込まれるべきです。{がどこで始まり、}がどこで終わったか分からないコードを書いたことがありませんか? すべての{}または()が異なるカラーで表示されるので、コードが見やすくなります。
indent-rainbow
インデントのすべてのタブやスペースに異なるカラーを加える機能拡張です。コードをより速くナビゲートするためにも役立ちます。
DotENV
.envファイルは最近一般的になっており、多くのプロジェクトで使用されています。VSCodeの初期状態ではこれらのファイルタイプに対する構文強調表示がサポートされていません。この機能拡張は、.envファイルに対するサポートを追加します。
Import Cost
この機能拡張はパフォーマンスとスピードを重視するすべてのデベロッパーにとって必須です。これは、このnpmモジュールをインポートするのにかかるコスト、またはインポートできるものがどれだけあるかをKbで示します。
Remote Development
これは私にとって重要な機能拡張です。SSHを介して開発環境に接続していますか? Dockerコンテナを使用していますか? それともWSLを使いますか? これを使用すると、すべてを非常に簡単に使用できます。一番いいのは、SSHを使ってもファイルの検索も開くのも遅くなりません。魔法みたいなこの機能拡張が大好きです!
Visual Studio IntelliCode
これはIntellisenseに代わるもので、AIベースのIntellisenseのようなものです。タブ補完では、他のユーザが使用している関数やパラメータのうち、最も使用されているものが一番上に表示されます。この機能拡張を使うことで、効率がアップします。
ESLint
この機能拡張をインストールすると、VSCodeに直接ESLintサポートが追加され、ESLintの問題を修正するのに役立ちます。これも必須アイテムです。
VSCodeの設定
VSCodeの設定は歯車アイコンの設定からではなく、JSONを使うことを私は好みます。JSONを使うには、Ctrl+Shift+Pを押して、「open settings JSON」と入力します。これで、settings.jsonファイルが開くので、他のJSONファイルと同じように編集することができます。
カーソルを見やすくする
"editor.cursorBlinking": "smooth",
"editor.cursorSmoothCaretAnimation": true,
"editor.cursorStyle": "block",
1行目は、fade-in-outのアニメーションでカーソルが滑らかに点滅します。 2行目は、カーソルの動きがアニメーションするので、自然に感じられます。 3行目は、カーソルの外観をブロックに変え、より快適に見つけることができます。 これらの設定でカーソルの表示が大幅に改善されました。
フォントを読みやすくする
"editor.fontFamily": "Hasklig, Consolas, 'Courier New', monospace",
"editor.fontLigatures": true,
"editor.fontSize": 15,
前述したように、私はフォントにHaskligを使用しています。また、合字を好み、これはコードが読みやすくなります。私は完璧な視力をもっていないので、フォントは少し大きめの方がよいです。
コードのコピペ時のフォーマット
"editor.formatOnPaste": true,
私はたまにネットからコードをコピペすることがあります。ペースト時の書式設定をしておきます。
ガイドラインの可視化
"editor.renderIndentGuides": true,
コードでたくさんのインデントを使うようであれば、ガイドラインがあると見やすくなります。
テレメトリを無効にする
"telemetry.enableTelemetry": false,
vscode-insidersを使用している場合でも、自分のデータをMicrosoftに送信する必要はありません。だから私は無効にします。
未保存のタブを分かりやすくする
"workbench.editor.highlightModifiedTabs": true,
VSCodeでは保存されていないファイルには、タブに大きなドットが表示されます。これだけでは分かりにくいので、ラインが追加されるように設定します。
VSCodeの便利な使い方
WSLを使う
Linux用のWSLまたはWindowsサブシステムは、Windows上でLinuxを実行するための素晴らしい方法です。私にとってはWindows上のものよりもはるかに優れたLinuxの開発体験を得ることができます。私はgitをwindowsにインストールしたくないし、nodejsのようなものもインストールしたくありません。
wsl LinuxのgitバージョンをVSCodeに統合する、かなり便利な方法があります。WSLGITをダウンロードし、適切な場所にexeファイルを置き、VSCodeの設定に下記を追加します。
"git.path": "D:\\dev\\wslgit.exe",
また、VSCodeには統合ターミナルがあります。ctrl+`でターミナルを開き、VSCodeにbashを追加して、下記を追加します。
"terminal.integrated.shell.windows": "C:\\WINDOWS\\System32\\wsl.exe",
これにより、VSCodeはデフォルトのwsl linuxシェルを使うようになります。私の場合は、bashに付属しているUbuntuです。
コマンドパレットを使う
VSCodeで設定やその他の機能を見つけるために、あちこちをクリックする人をよく見かけます。しかし、通常はコマンドを入力した方が簡単です。VSCodeにはコマンドパレットがあり、Ctrl+Shift+Pを押すだけで呼び出せます。
Shiftを押したくない場合は、Ctrlを押しながらPを押し、ボックスに「>」を入力するとすばやくコマンドパレットが起動します。Ctrl+Pは、必要なファイルを見つけるのにも便利です。
最近閉じたファイルをもう一度開く
Ctrl+Shift+Tを押すだけです。これで最後に閉じたファイルをもう一度開くことができます。また、複数回押すと、複数のファイルを開くことができます。 このショートカットは、ブラウザのタブでも動作します。
単語を削除する
一つの文字でなく、単語を削除するには、Ctrl+Backspaceを押します。これでもうBackspaceを何回も押す必要はありません。 このショートカットも他のソフトウェアで使用できます。
行を削除する
行全体を削除したい場合は、Ctrl+Xがお勧めです。いわゆるカットですが、現在選択されている行を完全に削除します。
あなたはVSCodeを使用していますか? 持っている機能拡張は何ですか? 見逃している設定はありますか? よく使うショートカットはありますか? 何かあれば、私に教えてください。
ここまで読んでくれて、ありがとうございます!
0 notes
Video
youtube
Reading Environment Variable in Nodejs JavaScript Application Using dote... Full Video Link https://youtu.be/dxrNopL1sbQHello friends, new #video on #reading #accessing #environmentvariables in #nodejs #projeect #application #tutorial #examples is published on #codeonedigest #youtube channel. @java #java #aws #awscloud @awscloud @AWSCloudIndia #salesforce #Cloud #CloudComputing @YouTube #youtube #azure #msazure #codeonedigest @codeonedigest #environmentvariables #nodejs #dotenv #setenvironmentvariables #linuxenvironmentvariables #pythonenvironmentvariables #dotenvnodejs #dotenvinnodejs #dotenvtutorial #dotenvtutorialnodejs #dotenvexample #dotenvmodule #dotenvmoduleinnodejs #dotenvmodulenotfound #dotenvpackage #dotenvpackagenodejs #dotenvpackage.json #dotenvpackageinstall #nodejsjavascript #environmentvariable #nodejsjavascripttutorial #javascriptenvironmentvariable #javascript
#youtube#nodejs#javascript#envrionment variable#nodejs tutorial#javascript tutorial#dotenv#dotenv tutorial
1 note
·
View note
Photo

Managing Environment Variables in Node.js with dotenv https://morioh.com/p/530dc5335dc5
#nodejs #js #env #javascript #development
0 notes
Text
使用@nestjs/config在NestJs中实现项目配置
1.Nodejs项目配置与dotenv
项目可能会运行在不同的环境下。不同的项目可能需要配置不同的端口、数据库等参数以保证项目运行,同一个项目在开发和测试阶段,也可能需要通过修改配置项来模拟接口条件。在生产环境中,一般通过将配置文件写入环境变量的方式来保证项目运行的安全性和可靠性。但在项目开发阶段,频繁修改环境变量则可能带来很多不必要的麻烦,因此,通过配置文件来实现每个项目的独立配置,既有利于前期开发,也便于后期的调试和部署。
在Nodejs项目中,操作系统中的环境变量可以通过process.env变量读取,这也是生产环境中的常规做法,而在开发和测试环境中,则更多地通过dotenv包来管理环境变量。在nodejs项目根目录下创建.env文件,并安装dotenv读取的基本流程如下:
安装dotenv npm install --save dotenv
在项目中导入doten…
View On WordPress
0 notes
Text
Creating Secure Password Flows With NodeJS And MySQL
About The Author
Darshan Somashekar is a technology entrepreneur who has built & sold two startups. His latest fun project is a solitaire site called Solitaired. Previously, … More about Darshan …
Reset password functionality is table stakes for any user-friendly application. It can also be a security nightmare. Using NodeJS and MySQL, Darshan demonstrates how to successfully create a secure reset password flow so you can avoid these pitfalls.
If you’re anything like me, you’ve forgotten your password more than once, especially on sites you haven’t visited in a while. You’ve probably also seen, and/or been mortified by, reset password emails that contain your password in plain text.
Unfortunately, the reset password workflow gets short shrift and limited attention during application development. This not only can lead to a frustrating user experience, but can also leave your application with gaping security holes.
We’re going to cover how to build a secure reset password workflow. We’ll be using NodeJS and MySQL as our base components. If you’re writing using a different language, framework, or database, you can still benefit from following the general “Security Tips” outlined in each section.
A reset password flow consists of the following components:
A link to send the user to the start of the workflow.
A form that lets the user submit their email.
A lookup that validates the email and sends an email to the address.
An email that contains the reset token with an expiry that allows the user to reset their password.
A form that let’s the user generate a new password.
Saving the new password and letting the user log in again with the new password.
Besides Node, Express & MySQL, we’ll be using the following libraries:
Sequelize ORM
Nodemailer
Sequelize is a NodeJS database ORM that makes it easier to run database migrations as well as security create queries. Nodemailer is a popular NodeJS email library that we’ll use to send password reset emails.
Security Tip #1
Some articles suggest secure password flows can be designed using JSON Web Tokens (JWT), which eliminate the need for database storage (and thus are easier to implement). We don’t use this approach on our site, because JWT token secrets are usually stored right in code. We want to avoid having ‘one secret’ to rule them all (for the same reason you don’t salt passwords with the same value), and therefore need to move this information into a database.
Installation
First, install Sequelize, Nodemailer, and other associated libraries:
$ npm install --save sequelize sequelize-cli mysql crypto nodemailer
In the route where you want to include your reset workflows, add the required modules. If you need a refresher on Express and routes, check out their guide.
const nodemailer = require('nodemailer');
And configure it with your email SMTP credentials.
const transport = nodemailer.createTransport({ host: process.env.EMAIL_HOST, port: process.env.EMAIL_PORT, secure: true, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS } });
The email solution I’m using is AWS’s Simple Email Service, but you can use anything (Mailgun, etc).
If this is your first time setting up your email sending service, you’ll need to spend some time configuring the appropriate Domain Keys and setting up authorizations. If you use Route 53 along with SES, this is super simple and done virtually automatically, which is why I picked it. AWS has some tutorials on how SES works with Route53.
Security tip #2
To store the credentials away from my code, I use dotenv, which lets me create a local .env file with my environment variables. That way, when I deploy to production, I can use different production keys that aren’t visible in code, and therefore lets me restrict permissions of my configuration to only certain members of my team.
Database Setup
Since we’re going to be sending reset tokens to users, we need to store those tokens in a database.
I am assuming you have a functioning users table in your database. If you’re using Sequelize already, great! If not, you may want to brush up on Sequelize and the Sequelize CLI.
If you haven’t used Sequelize yet in your app, you can set it up by running the command below in your app’s root folder:
$ sequelize init
This will create a number of new folders in your setup, including migrations and models.
This will also create a config file. In your config file, update the development block with the credentials to your local mysql database server.
Let’s use Sequelize’s CLI tool to generate the database table for us.
$ sequelize model:create --name ResetToken --attributes email:string,token:string,expiration:date,used:integer $ sequelize db:migrate
This table has the following columns:
Email address of user,
Token that has been generated,
Expiration of that token,
Whether the token has been used or not.
In the background, sequelize-cli is running the following SQL query:
CREATE TABLE `ResetTokens` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) DEFAULT NULL, `token` varchar(255) DEFAULT NULL, `expiration` datetime DEFAULT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, `used` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Verify this worked properly using your SQL client or the command line:
mysql> describe ResetTokens; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | email | varchar(255) | YES | | NULL | | | token | varchar(255) | YES | | NULL | | | expiration | datetime | YES | | NULL | | | createdAt | datetime | NO | | NULL | | | updatedAt | datetime | NO | | NULL | | | used | int(11) | NO | | 0 | | +------------+--------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec)
Security Tip #3
If you’re not currently using an ORM, you should consider doing so. An ORM automates the writing and proper escaping of SQL queries, making your code more readable and more secure by default. They’ll help you avoid SQL injection attacks by properly escaping your SQL queries.
Set Up Reset Password Route
Create the get route in user.js:
router.get('/forgot-password', function(req, res, next) { res.render('user/forgot-password', { }); });
Then create the POST route, which is the route that is hit when the reset password form is posted. In the code below, I’ve included a couple of important security features.
Security Tips #4-6
Even if we don’t find an email address, we return ‘ok’ as our status. We don’t want untoward bots figuring out what emails are real vs not real in our database.
The more random bytes you use in a token, the less likely it can be hacked. We are using 64 random bytes in our token generator (do not use less than 8).
Expire the token in 1 hour. This limits the window of time the reset token works.
router.post('/forgot-password', async function(req, res, next) { //ensure that you have a user with this email var email = await User.findOne({where: { email: req.body.email }}); if (email == null) { /** * we don't want to tell attackers that an * email doesn't exist, because that will let * them use this form to find ones that do * exist. **/ return res.json({status: 'ok'}); } /** * Expire any tokens that were previously * set for this user. That prevents old tokens * from being used. **/ await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); //Create a random reset token var fpSalt = crypto.randomBytes(64).toString('base64'); //token expires after one hour var expireDate = new Date(); expireDate.setDate(expireDate.getDate() + 1/24); //insert token data into DB await ResetToken.create({ email: req.body.email, expiration: expireDate, token: token, used: 0 }); //create email const message = { from: process.env.SENDER_ADDRESS, to: req.body.email, replyTo: process.env.REPLYTO_ADDRESS, subject: process.env.FORGOT_PASS_SUBJECT_LINE, text: 'To reset your password, please click the link below.\n\nhttps://'+process.env.DOMAIN+'/user/reset-password?token='+encodeURIComponent(token)+'&email='+req.body.email }; //send email transport.sendMail(message, function (err, info) { if(err) { console.log(err)} else { console.log(info); } }); return res.json({status: 'ok'}); });
You’ll see a User variable referenced above — what is this? For the purposes of this tutorial, we’re assuming you have a User model that connects to your database to retrieve values. The code above is based on Sequelize, but you can modify as needed if you query the database directly (but I recommend Sequelize!).
We now need to generate the view. Using Bootstrap CSS, jQuery, and the pug framework built into the Node Express framework, the view looks like the following:
extends ../layout block content div.container div.row div.col h1 Forgot password p Enter your email address below. If we have it on file, we will send you a reset email. div.forgot-message.alert.alert-success(style="display:none;") Email address received. If you have an email on file we will send you a reset email. Please wait a few minutes and check your spam folder if you don't see it. form#forgotPasswordForm.form-inline(onsubmit="return false;") div.form-group label.sr-only(for="email") Email address: input.form-control.mr-2#emailFp(type='email', name='email', placeholder="Email address") div.form-group.mt-1.text-center button#fpButton.btn.btn-success.mb-2(type='submit') Send email script. $('#fpButton').on('click', function() { $.post('/user/forgot-password', { email: $('#emailFp').val(), }, function(resp) { $('.forgot-message').show(); $('#forgotPasswordForm').remove(); }); });
Here’s the form on the page:
Your reset password form. (Large preview)
At this point, you should be able to fill out the form with an email address that’s in your database, and then receive a reset password email at that address. Clicking the reset link won’t do anything yet.
Set Up “Reset Password” Route
Now let’s go ahead and set up the rest of the workflow.
Add the Sequelize.Op module to your route:
const Sequelize = require('sequelize'); const Op = Sequelize.Op;
Now let’s build the GET route for users that have clicked on that reset password link. As you’ll see below, we want to make sure we’re validating the reset token appropriately.
Security Tip #7:
Ensure you’re only looking up reset tokens that have not expired and have not been used.
For demonstration purposes, I also clear up all expired tokens on load here to keep the table small. If you have a large website, move this to a cronjob.
router.get('/reset-password', async function(req, res, next) { /** * This code clears all expired tokens. You * should move this to a cronjob if you have a * big site. We just include this in here as a * demonstration. **/ await ResetToken.destroy({ where: { expiration: { [Op.lt]: Sequelize.fn('CURDATE')}, } }); //find the token var record = await ResetToken.findOne({ where: { email: req.query.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.query.token, used: 0 } }); if (record == null) { return res.render('user/reset-password', { message: 'Token has expired. Please try password reset again.', showForm: false }); } res.render('user/reset-password', { showForm: true, record: record }); });
Now let’s create the POST route which is what is hit once the user fills out their new password details.
Security tip #8 through 11:
Make sure that the passwords match and meet your minimum requirements.
Check the reset token again to make sure it has not been used and has not expired. We need to check it again because the token is being sent by a user via the form.
Before resetting the password, mark the token as used. That way, if something unforeseen happens (server crash, for example), the password won’t be reset while the token is still valid.
Use a cryptographically secure random salt (in this case, we use 64 random bytes).
router.post('/reset-password', async function(req, res, next) { //compare passwords if (req.body.password1 !== req.body.password2) { return res.json({status: 'error', message: 'Passwords do not match. Please try again.'}); } /** * Ensure password is valid (isValidPassword * function checks if password is >= 8 chars, alphanumeric, * has special chars, etc) **/ if (!isValidPassword(req.body.password1)) { return res.json({status: 'error', message: 'Password does not meet minimum requirements. Please try again.'}); } var record = await ResetToken.findOne({ where: { email: req.body.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.body.token, used: 0 } }); if (record == null) { return res.json({status: 'error', message: 'Token not found. Please try the reset password process again.'}); } var upd = await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); var newSalt = crypto.randomBytes(64).toString('hex'); var newPassword = crypto.pbkdf2Sync(req.body.password1, newSalt, 10000, 64, 'sha512').toString('base64'); await User.update({ password: newPassword, salt: newSalt }, { where: { email: req.body.email } }); return res.json({status: 'ok', message: 'Password reset. Please login with your new password.'}); }); And again, the view: extends ../layout block content div.container div.row div.col h1 Reset password p Enter your new password below. if message div.reset-message.alert.alert-warning #{message} else div.reset-message.alert(style='display:none;') if showForm form#resetPasswordForm(onsubmit="return false;") div.form-group label(for="password1") New password: input.form-control#password1(type='password', name='password1') small.form-text.text-muted Password must be 8 characters or more. div.form-group label(for="password2") Confirm new password input.form-control#password2(type='password', name='password2') small.form-text.text-muted Both passwords must match. input#emailRp(type='hidden', name='email', value=record.email) input#tokenRp(type='hidden', name='token', value=record.token) div.form-group button#rpButton.btn.btn-success(type='submit') Reset password script. $('#rpButton').on('click', function() { $.post('/user/reset-password', { password1: $('#password1').val(), password2: $('#password2').val(), email: $('#emailRp').val(), token: $('#tokenRp').val() }, function(resp) { if (resp.status == 'ok') { $('.reset-message').removeClass('alert-danger').addClass('alert-success').show().text(resp.message); $('#resetPasswordForm').remove(); } else { $('.reset-message').removeClass('alert-success').addClass('alert-danger').show().text(resp.message); } }); });
This is what it should look like:
Your reset password form. (Large preview)
Add The Link To Your Login Page
Lastly, don’t forget to add a link to this flow from your login page! Once you do this, you should have a working reset password flow. Be sure to test thoroughly at each stage of the process to confirm everything works and your tokens have a short expiration and are marked with the correct status as the workflow progresses.
Next Steps
Hopefully this helped you on your way to coding a secure, user-friendly reset password feature.
If you are interested in learning more about cryptographic security, I recommend Wikipedia’s summary (warning, it’s dense!).
If you want to add even more security to your app’s authentication, look into 2FA. There are a lot of different options out there.
If I’ve scared you off from building your own reset password flow, you can rely on third-party login systems like Google and Facebook. PassportJS is a middleware you can use for NodeJS that implements these strategies.
(dm, yk, il)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/creating-secure-password-flows-with-nodejs-and-mysql/ source https://scpie.tumblr.com/post/612405639958986752
0 notes
Text
Creating Secure Password Flows With NodeJS And MySQL
About The Author
Darshan Somashekar is a technology entrepreneur who has built & sold two startups. His latest fun project is a solitaire site called Solitaired. Previously, … More about Darshan …
Reset password functionality is table stakes for any user-friendly application. It can also be a security nightmare. Using NodeJS and MySQL, Darshan demonstrates how to successfully create a secure reset password flow so you can avoid these pitfalls.
If you’re anything like me, you’ve forgotten your password more than once, especially on sites you haven’t visited in a while. You’ve probably also seen, and/or been mortified by, reset password emails that contain your password in plain text.
Unfortunately, the reset password workflow gets short shrift and limited attention during application development. This not only can lead to a frustrating user experience, but can also leave your application with gaping security holes.
We’re going to cover how to build a secure reset password workflow. We’ll be using NodeJS and MySQL as our base components. If you’re writing using a different language, framework, or database, you can still benefit from following the general “Security Tips” outlined in each section.
A reset password flow consists of the following components:
A link to send the user to the start of the workflow.
A form that lets the user submit their email.
A lookup that validates the email and sends an email to the address.
An email that contains the reset token with an expiry that allows the user to reset their password.
A form that let’s the user generate a new password.
Saving the new password and letting the user log in again with the new password.
Besides Node, Express & MySQL, we’ll be using the following libraries:
Sequelize ORM
Nodemailer
Sequelize is a NodeJS database ORM that makes it easier to run database migrations as well as security create queries. Nodemailer is a popular NodeJS email library that we’ll use to send password reset emails.
Security Tip #1
Some articles suggest secure password flows can be designed using JSON Web Tokens (JWT), which eliminate the need for database storage (and thus are easier to implement). We don’t use this approach on our site, because JWT token secrets are usually stored right in code. We want to avoid having ‘one secret’ to rule them all (for the same reason you don’t salt passwords with the same value), and therefore need to move this information into a database.
Installation
First, install Sequelize, Nodemailer, and other associated libraries:
$ npm install --save sequelize sequelize-cli mysql crypto nodemailer
In the route where you want to include your reset workflows, add the required modules. If you need a refresher on Express and routes, check out their guide.
const nodemailer = require('nodemailer');
And configure it with your email SMTP credentials.
const transport = nodemailer.createTransport({ host: process.env.EMAIL_HOST, port: process.env.EMAIL_PORT, secure: true, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS } });
The email solution I’m using is AWS’s Simple Email Service, but you can use anything (Mailgun, etc).
If this is your first time setting up your email sending service, you’ll need to spend some time configuring the appropriate Domain Keys and setting up authorizations. If you use Route 53 along with SES, this is super simple and done virtually automatically, which is why I picked it. AWS has some tutorials on how SES works with Route53.
Security tip #2
To store the credentials away from my code, I use dotenv, which lets me create a local .env file with my environment variables. That way, when I deploy to production, I can use different production keys that aren’t visible in code, and therefore lets me restrict permissions of my configuration to only certain members of my team.
Database Setup
Since we’re going to be sending reset tokens to users, we need to store those tokens in a database.
I am assuming you have a functioning users table in your database. If you’re using Sequelize already, great! If not, you may want to brush up on Sequelize and the Sequelize CLI.
If you haven’t used Sequelize yet in your app, you can set it up by running the command below in your app’s root folder:
$ sequelize init
This will create a number of new folders in your setup, including migrations and models.
This will also create a config file. In your config file, update the development block with the credentials to your local mysql database server.
Let’s use Sequelize’s CLI tool to generate the database table for us.
$ sequelize model:create --name ResetToken --attributes email:string,token:string,expiration:date,used:integer $ sequelize db:migrate
This table has the following columns:
Email address of user,
Token that has been generated,
Expiration of that token,
Whether the token has been used or not.
In the background, sequelize-cli is running the following SQL query:
CREATE TABLE `ResetTokens` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) DEFAULT NULL, `token` varchar(255) DEFAULT NULL, `expiration` datetime DEFAULT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, `used` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Verify this worked properly using your SQL client or the command line:
mysql> describe ResetTokens; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | email | varchar(255) | YES | | NULL | | | token | varchar(255) | YES | | NULL | | | expiration | datetime | YES | | NULL | | | createdAt | datetime | NO | | NULL | | | updatedAt | datetime | NO | | NULL | | | used | int(11) | NO | | 0 | | +------------+--------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec)
Security Tip #3
If you’re not currently using an ORM, you should consider doing so. An ORM automates the writing and proper escaping of SQL queries, making your code more readable and more secure by default. They’ll help you avoid SQL injection attacks by properly escaping your SQL queries.
Set Up Reset Password Route
Create the get route in user.js:
router.get('/forgot-password', function(req, res, next) { res.render('user/forgot-password', { }); });
Then create the POST route, which is the route that is hit when the reset password form is posted. In the code below, I’ve included a couple of important security features.
Security Tips #4-6
Even if we don’t find an email address, we return ‘ok’ as our status. We don’t want untoward bots figuring out what emails are real vs not real in our database.
The more random bytes you use in a token, the less likely it can be hacked. We are using 64 random bytes in our token generator (do not use less than 8).
Expire the token in 1 hour. This limits the window of time the reset token works.
router.post('/forgot-password', async function(req, res, next) { //ensure that you have a user with this email var email = await User.findOne({where: { email: req.body.email }}); if (email == null) { /** * we don't want to tell attackers that an * email doesn't exist, because that will let * them use this form to find ones that do * exist. **/ return res.json({status: 'ok'}); } /** * Expire any tokens that were previously * set for this user. That prevents old tokens * from being used. **/ await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); //Create a random reset token var fpSalt = crypto.randomBytes(64).toString('base64'); //token expires after one hour var expireDate = new Date(); expireDate.setDate(expireDate.getDate() + 1/24); //insert token data into DB await ResetToken.create({ email: req.body.email, expiration: expireDate, token: token, used: 0 }); //create email const message = { from: process.env.SENDER_ADDRESS, to: req.body.email, replyTo: process.env.REPLYTO_ADDRESS, subject: process.env.FORGOT_PASS_SUBJECT_LINE, text: 'To reset your password, please click the link below.\n\nhttps://'+process.env.DOMAIN+'/user/reset-password?token='+encodeURIComponent(token)+'&email='+req.body.email }; //send email transport.sendMail(message, function (err, info) { if(err) { console.log(err)} else { console.log(info); } }); return res.json({status: 'ok'}); });
You’ll see a User variable referenced above — what is this? For the purposes of this tutorial, we’re assuming you have a User model that connects to your database to retrieve values. The code above is based on Sequelize, but you can modify as needed if you query the database directly (but I recommend Sequelize!).
We now need to generate the view. Using Bootstrap CSS, jQuery, and the pug framework built into the Node Express framework, the view looks like the following:
extends ../layout block content div.container div.row div.col h1 Forgot password p Enter your email address below. If we have it on file, we will send you a reset email. div.forgot-message.alert.alert-success(style="display:none;") Email address received. If you have an email on file we will send you a reset email. Please wait a few minutes and check your spam folder if you don't see it. form#forgotPasswordForm.form-inline(onsubmit="return false;") div.form-group label.sr-only(for="email") Email address: input.form-control.mr-2#emailFp(type='email', name='email', placeholder="Email address") div.form-group.mt-1.text-center button#fpButton.btn.btn-success.mb-2(type='submit') Send email script. $('#fpButton').on('click', function() { $.post('/user/forgot-password', { email: $('#emailFp').val(), }, function(resp) { $('.forgot-message').show(); $('#forgotPasswordForm').remove(); }); });
Here’s the form on the page:
Your reset password form. (Large preview)
At this point, you should be able to fill out the form with an email address that’s in your database, and then receive a reset password email at that address. Clicking the reset link won’t do anything yet.
Set Up “Reset Password” Route
Now let’s go ahead and set up the rest of the workflow.
Add the Sequelize.Op module to your route:
const Sequelize = require('sequelize'); const Op = Sequelize.Op;
Now let’s build the GET route for users that have clicked on that reset password link. As you’ll see below, we want to make sure we’re validating the reset token appropriately.
Security Tip #7:
Ensure you’re only looking up reset tokens that have not expired and have not been used.
For demonstration purposes, I also clear up all expired tokens on load here to keep the table small. If you have a large website, move this to a cronjob.
router.get('/reset-password', async function(req, res, next) { /** * This code clears all expired tokens. You * should move this to a cronjob if you have a * big site. We just include this in here as a * demonstration. **/ await ResetToken.destroy({ where: { expiration: { [Op.lt]: Sequelize.fn('CURDATE')}, } }); //find the token var record = await ResetToken.findOne({ where: { email: req.query.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.query.token, used: 0 } }); if (record == null) { return res.render('user/reset-password', { message: 'Token has expired. Please try password reset again.', showForm: false }); } res.render('user/reset-password', { showForm: true, record: record }); });
Now let’s create the POST route which is what is hit once the user fills out their new password details.
Security tip #8 through 11:
Make sure that the passwords match and meet your minimum requirements.
Check the reset token again to make sure it has not been used and has not expired. We need to check it again because the token is being sent by a user via the form.
Before resetting the password, mark the token as used. That way, if something unforeseen happens (server crash, for example), the password won’t be reset while the token is still valid.
Use a cryptographically secure random salt (in this case, we use 64 random bytes).
router.post('/reset-password', async function(req, res, next) { //compare passwords if (req.body.password1 !== req.body.password2) { return res.json({status: 'error', message: 'Passwords do not match. Please try again.'}); } /** * Ensure password is valid (isValidPassword * function checks if password is >= 8 chars, alphanumeric, * has special chars, etc) **/ if (!isValidPassword(req.body.password1)) { return res.json({status: 'error', message: 'Password does not meet minimum requirements. Please try again.'}); } var record = await ResetToken.findOne({ where: { email: req.body.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.body.token, used: 0 } }); if (record == null) { return res.json({status: 'error', message: 'Token not found. Please try the reset password process again.'}); } var upd = await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); var newSalt = crypto.randomBytes(64).toString('hex'); var newPassword = crypto.pbkdf2Sync(req.body.password1, newSalt, 10000, 64, 'sha512').toString('base64'); await User.update({ password: newPassword, salt: newSalt }, { where: { email: req.body.email } }); return res.json({status: 'ok', message: 'Password reset. Please login with your new password.'}); }); And again, the view: extends ../layout block content div.container div.row div.col h1 Reset password p Enter your new password below. if message div.reset-message.alert.alert-warning #{message} else div.reset-message.alert(style='display:none;') if showForm form#resetPasswordForm(onsubmit="return false;") div.form-group label(for="password1") New password: input.form-control#password1(type='password', name='password1') small.form-text.text-muted Password must be 8 characters or more. div.form-group label(for="password2") Confirm new password input.form-control#password2(type='password', name='password2') small.form-text.text-muted Both passwords must match. input#emailRp(type='hidden', name='email', value=record.email) input#tokenRp(type='hidden', name='token', value=record.token) div.form-group button#rpButton.btn.btn-success(type='submit') Reset password script. $('#rpButton').on('click', function() { $.post('/user/reset-password', { password1: $('#password1').val(), password2: $('#password2').val(), email: $('#emailRp').val(), token: $('#tokenRp').val() }, function(resp) { if (resp.status == 'ok') { $('.reset-message').removeClass('alert-danger').addClass('alert-success').show().text(resp.message); $('#resetPasswordForm').remove(); } else { $('.reset-message').removeClass('alert-success').addClass('alert-danger').show().text(resp.message); } }); });
This is what it should look like:
Your reset password form. (Large preview)
Add The Link To Your Login Page
Lastly, don’t forget to add a link to this flow from your login page! Once you do this, you should have a working reset password flow. Be sure to test thoroughly at each stage of the process to confirm everything works and your tokens have a short expiration and are marked with the correct status as the workflow progresses.
Next Steps
Hopefully this helped you on your way to coding a secure, user-friendly reset password feature.
If you are interested in learning more about cryptographic security, I recommend Wikipedia’s summary (warning, it’s dense!).
If you want to add even more security to your app’s authentication, look into 2FA. There are a lot of different options out there.
If I’ve scared you off from building your own reset password flow, you can rely on third-party login systems like Google and Facebook. PassportJS is a middleware you can use for NodeJS that implements these strategies.
(dm, yk, il)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/creating-secure-password-flows-with-nodejs-and-mysql/
0 notes
Text
Creating Secure Password Flows With NodeJS And MySQL
About The Author
Darshan Somashekar is a technology entrepreneur who has built & sold two startups. His latest fun project is a solitaire site called Solitaired. Previously, … More about Darshan …
Reset password functionality is table stakes for any user-friendly application. It can also be a security nightmare. Using NodeJS and MySQL, Darshan demonstrates how to successfully create a secure reset password flow so you can avoid these pitfalls.
If you’re anything like me, you’ve forgotten your password more than once, especially on sites you haven’t visited in a while. You’ve probably also seen, and/or been mortified by, reset password emails that contain your password in plain text.
Unfortunately, the reset password workflow gets short shrift and limited attention during application development. This not only can lead to a frustrating user experience, but can also leave your application with gaping security holes.
We’re going to cover how to build a secure reset password workflow. We’ll be using NodeJS and MySQL as our base components. If you’re writing using a different language, framework, or database, you can still benefit from following the general “Security Tips” outlined in each section.
A reset password flow consists of the following components:
A link to send the user to the start of the workflow.
A form that lets the user submit their email.
A lookup that validates the email and sends an email to the address.
An email that contains the reset token with an expiry that allows the user to reset their password.
A form that let’s the user generate a new password.
Saving the new password and letting the user log in again with the new password.
Besides Node, Express & MySQL, we’ll be using the following libraries:
Sequelize ORM
Nodemailer
Sequelize is a NodeJS database ORM that makes it easier to run database migrations as well as security create queries. Nodemailer is a popular NodeJS email library that we’ll use to send password reset emails.
Security Tip #1
Some articles suggest secure password flows can be designed using JSON Web Tokens (JWT), which eliminate the need for database storage (and thus are easier to implement). We don’t use this approach on our site, because JWT token secrets are usually stored right in code. We want to avoid having ‘one secret’ to rule them all (for the same reason you don’t salt passwords with the same value), and therefore need to move this information into a database.
Installation
First, install Sequelize, Nodemailer, and other associated libraries:
$ npm install --save sequelize sequelize-cli mysql crypto nodemailer
In the route where you want to include your reset workflows, add the required modules. If you need a refresher on Express and routes, check out their guide.
const nodemailer = require('nodemailer');
And configure it with your email SMTP credentials.
const transport = nodemailer.createTransport({ host: process.env.EMAIL_HOST, port: process.env.EMAIL_PORT, secure: true, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS } });
The email solution I’m using is AWS’s Simple Email Service, but you can use anything (Mailgun, etc).
If this is your first time setting up your email sending service, you’ll need to spend some time configuring the appropriate Domain Keys and setting up authorizations. If you use Route 53 along with SES, this is super simple and done virtually automatically, which is why I picked it. AWS has some tutorials on how SES works with Route53.
Security tip #2
To store the credentials away from my code, I use dotenv, which lets me create a local .env file with my environment variables. That way, when I deploy to production, I can use different production keys that aren’t visible in code, and therefore lets me restrict permissions of my configuration to only certain members of my team.
Database Setup
Since we’re going to be sending reset tokens to users, we need to store those tokens in a database.
I am assuming you have a functioning users table in your database. If you’re using Sequelize already, great! If not, you may want to brush up on Sequelize and the Sequelize CLI.
If you haven’t used Sequelize yet in your app, you can set it up by running the command below in your app’s root folder:
$ sequelize init
This will create a number of new folders in your setup, including migrations and models.
This will also create a config file. In your config file, update the development block with the credentials to your local mysql database server.
Let’s use Sequelize’s CLI tool to generate the database table for us.
$ sequelize model:create --name ResetToken --attributes email:string,token:string,expiration:date,used:integer $ sequelize db:migrate
This table has the following columns:
Email address of user,
Token that has been generated,
Expiration of that token,
Whether the token has been used or not.
In the background, sequelize-cli is running the following SQL query:
CREATE TABLE `ResetTokens` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) DEFAULT NULL, `token` varchar(255) DEFAULT NULL, `expiration` datetime DEFAULT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, `used` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Verify this worked properly using your SQL client or the command line:
mysql> describe ResetTokens; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | email | varchar(255) | YES | | NULL | | | token | varchar(255) | YES | | NULL | | | expiration | datetime | YES | | NULL | | | createdAt | datetime | NO | | NULL | | | updatedAt | datetime | NO | | NULL | | | used | int(11) | NO | | 0 | | +------------+--------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec)
Security Tip #3
If you’re not currently using an ORM, you should consider doing so. An ORM automates the writing and proper escaping of SQL queries, making your code more readable and more secure by default. They’ll help you avoid SQL injection attacks by properly escaping your SQL queries.
Set Up Reset Password Route
Create the get route in user.js:
router.get('/forgot-password', function(req, res, next) { res.render('user/forgot-password', { }); });
Then create the POST route, which is the route that is hit when the reset password form is posted. In the code below, I’ve included a couple of important security features.
Security Tips #4-6
Even if we don’t find an email address, we return ‘ok’ as our status. We don’t want untoward bots figuring out what emails are real vs not real in our database.
The more random bytes you use in a token, the less likely it can be hacked. We are using 64 random bytes in our token generator (do not use less than 8).
Expire the token in 1 hour. This limits the window of time the reset token works.
router.post('/forgot-password', async function(req, res, next) { //ensure that you have a user with this email var email = await User.findOne({where: { email: req.body.email }}); if (email == null) { /** * we don't want to tell attackers that an * email doesn't exist, because that will let * them use this form to find ones that do * exist. **/ return res.json({status: 'ok'}); } /** * Expire any tokens that were previously * set for this user. That prevents old tokens * from being used. **/ await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); //Create a random reset token var fpSalt = crypto.randomBytes(64).toString('base64'); //token expires after one hour var expireDate = new Date(); expireDate.setDate(expireDate.getDate() + 1/24); //insert token data into DB await ResetToken.create({ email: req.body.email, expiration: expireDate, token: token, used: 0 }); //create email const message = { from: process.env.SENDER_ADDRESS, to: req.body.email, replyTo: process.env.REPLYTO_ADDRESS, subject: process.env.FORGOT_PASS_SUBJECT_LINE, text: 'To reset your password, please click the link below.\n\nhttps://'+process.env.DOMAIN+'/user/reset-password?token='+encodeURIComponent(token)+'&email='+req.body.email }; //send email transport.sendMail(message, function (err, info) { if(err) { console.log(err)} else { console.log(info); } }); return res.json({status: 'ok'}); });
You’ll see a User variable referenced above — what is this? For the purposes of this tutorial, we’re assuming you have a User model that connects to your database to retrieve values. The code above is based on Sequelize, but you can modify as needed if you query the database directly (but I recommend Sequelize!).
We now need to generate the view. Using Bootstrap CSS, jQuery, and the pug framework built into the Node Express framework, the view looks like the following:
extends ../layout block content div.container div.row div.col h1 Forgot password p Enter your email address below. If we have it on file, we will send you a reset email. div.forgot-message.alert.alert-success(style="display:none;") Email address received. If you have an email on file we will send you a reset email. Please wait a few minutes and check your spam folder if you don't see it. form#forgotPasswordForm.form-inline(onsubmit="return false;") div.form-group label.sr-only(for="email") Email address: input.form-control.mr-2#emailFp(type='email', name='email', placeholder="Email address") div.form-group.mt-1.text-center button#fpButton.btn.btn-success.mb-2(type='submit') Send email script. $('#fpButton').on('click', function() { $.post('/user/forgot-password', { email: $('#emailFp').val(), }, function(resp) { $('.forgot-message').show(); $('#forgotPasswordForm').remove(); }); });
Here’s the form on the page:
Your reset password form. (Large preview)
At this point, you should be able to fill out the form with an email address that’s in your database, and then receive a reset password email at that address. Clicking the reset link won’t do anything yet.
Set Up “Reset Password” Route
Now let’s go ahead and set up the rest of the workflow.
Add the Sequelize.Op module to your route:
const Sequelize = require('sequelize'); const Op = Sequelize.Op;
Now let’s build the GET route for users that have clicked on that reset password link. As you’ll see below, we want to make sure we’re validating the reset token appropriately.
Security Tip #7:
Ensure you’re only looking up reset tokens that have not expired and have not been used.
For demonstration purposes, I also clear up all expired tokens on load here to keep the table small. If you have a large website, move this to a cronjob.
router.get('/reset-password', async function(req, res, next) { /** * This code clears all expired tokens. You * should move this to a cronjob if you have a * big site. We just include this in here as a * demonstration. **/ await ResetToken.destroy({ where: { expiration: { [Op.lt]: Sequelize.fn('CURDATE')}, } }); //find the token var record = await ResetToken.findOne({ where: { email: req.query.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.query.token, used: 0 } }); if (record == null) { return res.render('user/reset-password', { message: 'Token has expired. Please try password reset again.', showForm: false }); } res.render('user/reset-password', { showForm: true, record: record }); });
Now let’s create the POST route which is what is hit once the user fills out their new password details.
Security tip #8 through 11:
Make sure that the passwords match and meet your minimum requirements.
Check the reset token again to make sure it has not been used and has not expired. We need to check it again because the token is being sent by a user via the form.
Before resetting the password, mark the token as used. That way, if something unforeseen happens (server crash, for example), the password won’t be reset while the token is still valid.
Use a cryptographically secure random salt (in this case, we use 64 random bytes).
router.post('/reset-password', async function(req, res, next) { //compare passwords if (req.body.password1 !== req.body.password2) { return res.json({status: 'error', message: 'Passwords do not match. Please try again.'}); } /** * Ensure password is valid (isValidPassword * function checks if password is >= 8 chars, alphanumeric, * has special chars, etc) **/ if (!isValidPassword(req.body.password1)) { return res.json({status: 'error', message: 'Password does not meet minimum requirements. Please try again.'}); } var record = await ResetToken.findOne({ where: { email: req.body.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.body.token, used: 0 } }); if (record == null) { return res.json({status: 'error', message: 'Token not found. Please try the reset password process again.'}); } var upd = await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); var newSalt = crypto.randomBytes(64).toString('hex'); var newPassword = crypto.pbkdf2Sync(req.body.password1, newSalt, 10000, 64, 'sha512').toString('base64'); await User.update({ password: newPassword, salt: newSalt }, { where: { email: req.body.email } }); return res.json({status: 'ok', message: 'Password reset. Please login with your new password.'}); }); And again, the view: extends ../layout block content div.container div.row div.col h1 Reset password p Enter your new password below. if message div.reset-message.alert.alert-warning #{message} else div.reset-message.alert(style='display:none;') if showForm form#resetPasswordForm(onsubmit="return false;") div.form-group label(for="password1") New password: input.form-control#password1(type='password', name='password1') small.form-text.text-muted Password must be 8 characters or more. div.form-group label(for="password2") Confirm new password input.form-control#password2(type='password', name='password2') small.form-text.text-muted Both passwords must match. input#emailRp(type='hidden', name='email', value=record.email) input#tokenRp(type='hidden', name='token', value=record.token) div.form-group button#rpButton.btn.btn-success(type='submit') Reset password script. $('#rpButton').on('click', function() { $.post('/user/reset-password', { password1: $('#password1').val(), password2: $('#password2').val(), email: $('#emailRp').val(), token: $('#tokenRp').val() }, function(resp) { if (resp.status == 'ok') { $('.reset-message').removeClass('alert-danger').addClass('alert-success').show().text(resp.message); $('#resetPasswordForm').remove(); } else { $('.reset-message').removeClass('alert-success').addClass('alert-danger').show().text(resp.message); } }); });
This is what it should look like:
Your reset password form. (Large preview)
Add The Link To Your Login Page
Lastly, don’t forget to add a link to this flow from your login page! Once you do this, you should have a working reset password flow. Be sure to test thoroughly at each stage of the process to confirm everything works and your tokens have a short expiration and are marked with the correct status as the workflow progresses.
Next Steps
Hopefully this helped you on your way to coding a secure, user-friendly reset password feature.
If you are interested in learning more about cryptographic security, I recommend Wikipedia’s summary (warning, it’s dense!).
If you want to add even more security to your app’s authentication, look into 2FA. There are a lot of different options out there.
If I’ve scared you off from building your own reset password flow, you can rely on third-party login systems like Google and Facebook. PassportJS is a middleware you can use for NodeJS that implements these strategies.
(dm, yk, il)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/creating-secure-password-flows-with-nodejs-and-mysql/ source https://scpie1.blogspot.com/2020/03/creating-secure-password-flows-with.html
0 notes
Text
Creating Secure Password Flows With NodeJS And MySQL
About The Author
Darshan Somashekar is a technology entrepreneur who has built & sold two startups. His latest fun project is a solitaire site called Solitaired. Previously, … More about Darshan …
Reset password functionality is table stakes for any user-friendly application. It can also be a security nightmare. Using NodeJS and MySQL, Darshan demonstrates how to successfully create a secure reset password flow so you can avoid these pitfalls.
If you’re anything like me, you’ve forgotten your password more than once, especially on sites you haven’t visited in a while. You’ve probably also seen, and/or been mortified by, reset password emails that contain your password in plain text.
Unfortunately, the reset password workflow gets short shrift and limited attention during application development. This not only can lead to a frustrating user experience, but can also leave your application with gaping security holes.
We’re going to cover how to build a secure reset password workflow. We’ll be using NodeJS and MySQL as our base components. If you’re writing using a different language, framework, or database, you can still benefit from following the general “Security Tips” outlined in each section.
A reset password flow consists of the following components:
A link to send the user to the start of the workflow.
A form that lets the user submit their email.
A lookup that validates the email and sends an email to the address.
An email that contains the reset token with an expiry that allows the user to reset their password.
A form that let’s the user generate a new password.
Saving the new password and letting the user log in again with the new password.
Besides Node, Express & MySQL, we’ll be using the following libraries:
Sequelize ORM
Nodemailer
Sequelize is a NodeJS database ORM that makes it easier to run database migrations as well as security create queries. Nodemailer is a popular NodeJS email library that we’ll use to send password reset emails.
Security Tip #1
Some articles suggest secure password flows can be designed using JSON Web Tokens (JWT), which eliminate the need for database storage (and thus are easier to implement). We don’t use this approach on our site, because JWT token secrets are usually stored right in code. We want to avoid having ‘one secret’ to rule them all (for the same reason you don’t salt passwords with the same value), and therefore need to move this information into a database.
Installation
First, install Sequelize, Nodemailer, and other associated libraries:
$ npm install --save sequelize sequelize-cli mysql crypto nodemailer
In the route where you want to include your reset workflows, add the required modules. If you need a refresher on Express and routes, check out their guide.
const nodemailer = require('nodemailer');
And configure it with your email SMTP credentials.
const transport = nodemailer.createTransport({ host: process.env.EMAIL_HOST, port: process.env.EMAIL_PORT, secure: true, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS }});
The email solution I’m using is AWS’s Simple Email Service, but you can use anything (Mailgun, etc).
If this is your first time setting up your email sending service, you’ll need to spend some time configuring the appropriate Domain Keys and setting up authorizations. If you use Route 53 along with SES, this is super simple and done virtually automatically, which is why I picked it. AWS has some tutorials on how SES works with Route53.
Security tip #2
To store the credentials away from my code, I use dotenv, which lets me create a local .env file with my environment variables. That way, when I deploy to production, I can use different production keys that aren’t visible in code, and therefore lets me restrict permissions of my configuration to only certain members of my team.
Database Setup
Since we’re going to be sending reset tokens to users, we need to store those tokens in a database.
I am assuming you have a functioning users table in your database. If you’re using Sequelize already, great! If not, you may want to brush up on Sequelize and the Sequelize CLI.
If you haven’t used Sequelize yet in your app, you can set it up by running the command below in your app’s root folder:
$ sequelize init
This will create a number of new folders in your setup, including migrations and models.
This will also create a config file. In your config file, update the development block with the credentials to your local mysql database server.
Let’s use Sequelize’s CLI tool to generate the database table for us.
$ sequelize model:create --name ResetToken --attributes email:string,token:string,expiration:date,used:integer$ sequelize db:migrate
This table has the following columns:
Email address of user,
Token that has been generated,
Expiration of that token,
Whether the token has been used or not.
In the background, sequelize-cli is running the following SQL query:
CREATE TABLE `ResetTokens` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) DEFAULT NULL, `token` varchar(255) DEFAULT NULL, `expiration` datetime DEFAULT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, `used` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Verify this worked properly using your SQL client or the command line:
mysql> describe ResetTokens;+------------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+------------+--------------+------+-----+---------+----------------+| id | int(11) | NO | PRI | NULL | auto_increment || email | varchar(255) | YES | | NULL | || token | varchar(255) | YES | | NULL | || expiration | datetime | YES | | NULL | || createdAt | datetime | NO | | NULL | || updatedAt | datetime | NO | | NULL | || used | int(11) | NO | | 0 | |+------------+--------------+------+-----+---------+----------------+7 rows in set (0.00 sec)
Security Tip #3
If you’re not currently using an ORM, you should consider doing so. An ORM automates the writing and proper escaping of SQL queries, making your code more readable and more secure by default. They’ll help you avoid SQL injection attacks by properly escaping your SQL queries.
Set Up Reset Password Route
Create the get route in user.js:
router.get('/forgot-password', function(req, res, next) { res.render('user/forgot-password', { });});
Then create the POST route, which is the route that is hit when the reset password form is posted. In the code below, I’ve included a couple of important security features.
Security Tips #4-6
Even if we don’t find an email address, we return ‘ok’ as our status. We don’t want untoward bots figuring out what emails are real vs not real in our database.
The more random bytes you use in a token, the less likely it can be hacked. We are using 64 random bytes in our token generator (do not use less than 8).
Expire the token in 1 hour. This limits the window of time the reset token works.
router.post('/forgot-password', async function(req, res, next) { //ensure that you have a user with this email var email = await User.findOne({where: { email: req.body.email }}); if (email == null) { /** * we don't want to tell attackers that an * email doesn't exist, because that will let * them use this form to find ones that do * exist. **/ return res.json({status: 'ok'}); } /** * Expire any tokens that were previously * set for this user. That prevents old tokens * from being used. **/ await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); //Create a random reset token var fpSalt = crypto.randomBytes(64).toString('base64'); //token expires after one hour var expireDate = new Date(); expireDate.setDate(expireDate.getDate() + 1/24); //insert token data into DB await ResetToken.create({ email: req.body.email, expiration: expireDate, token: token, used: 0 }); //create email const message = { from: process.env.SENDER_ADDRESS, to: req.body.email, replyTo: process.env.REPLYTO_ADDRESS, subject: process.env.FORGOT_PASS_SUBJECT_LINE, text: 'To reset your password, please click the link below.\n\nhttps://'+process.env.DOMAIN+'/user/reset-password?token='+encodeURIComponent(token)+'&email='+req.body.email }; //send email transport.sendMail(message, function (err, info) { if(err) { console.log(err)} else { console.log(info); } }); return res.json({status: 'ok'});});
You’ll see a User variable referenced above — what is this? For the purposes of this tutorial, we’re assuming you have a User model that connects to your database to retrieve values. The code above is based on Sequelize, but you can modify as needed if you query the database directly (but I recommend Sequelize!).
We now need to generate the view. Using Bootstrap CSS, jQuery, and the pug framework built into the Node Express framework, the view looks like the following:
extends ../layout block content div.container div.row div.col h1 Forgot password p Enter your email address below. If we have it on file, we will send you a reset email. div.forgot-message.alert.alert-success(style="display:none;") Email address received. If you have an email on file we will send you a reset email. Please wait a few minutes and check your spam folder if you don't see it. form#forgotPasswordForm.form-inline(onsubmit="return false;") div.form-group label.sr-only(for="email") Email address: input.form-control.mr-2#emailFp(type='email', name='email', placeholder="Email address") div.form-group.mt-1.text-center button#fpButton.btn.btn-success.mb-2(type='submit') Send email script. $('#fpButton').on('click', function() { $.post('/user/forgot-password', { email: $('#emailFp').val(), }, function(resp) { $('.forgot-message').show(); $('#forgotPasswordForm').remove(); }); });
Here’s the form on the page:
Your reset password form. (Large preview)
At this point, you should be able to fill out the form with an email address that’s in your database, and then receive a reset password email at that address. Clicking the reset link won’t do anything yet.
Set Up “Reset Password” Route
Now let’s go ahead and set up the rest of the workflow.
Add the Sequelize.Op module to your route:
const Sequelize = require('sequelize');const Op = Sequelize.Op;
Now let’s build the GET route for users that have clicked on that reset password link. As you’ll see below, we want to make sure we’re validating the reset token appropriately.
Security Tip #7:
Ensure you’re only looking up reset tokens that have not expired and have not been used.
For demonstration purposes, I also clear up all expired tokens on load here to keep the table small. If you have a large website, move this to a cronjob.
router.get('/reset-password', async function(req, res, next) { /** * This code clears all expired tokens. You * should move this to a cronjob if you have a * big site. We just include this in here as a * demonstration. **/ await ResetToken.destroy({ where: { expiration: { [Op.lt]: Sequelize.fn('CURDATE')}, } }); //find the token var record = await ResetToken.findOne({ where: { email: req.query.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.query.token, used: 0 } }); if (record == null) { return res.render('user/reset-password', { message: 'Token has expired. Please try password reset again.', showForm: false }); } res.render('user/reset-password', { showForm: true, record: record });});
Now let’s create the POST route which is what is hit once the user fills out their new password details.
Security tip #8 through 11:
Make sure that the passwords match and meet your minimum requirements.
Check the reset token again to make sure it has not been used and has not expired. We need to check it again because the token is being sent by a user via the form.
Before resetting the password, mark the token as used. That way, if something unforeseen happens (server crash, for example), the password won’t be reset while the token is still valid.
Use a cryptographically secure random salt (in this case, we use 64 random bytes).
router.post('/reset-password', async function(req, res, next) { //compare passwords if (req.body.password1 !== req.body.password2) { return res.json({status: 'error', message: 'Passwords do not match. Please try again.'}); } /** * Ensure password is valid (isValidPassword * function checks if password is >= 8 chars, alphanumeric, * has special chars, etc) **/ if (!isValidPassword(req.body.password1)) { return res.json({status: 'error', message: 'Password does not meet minimum requirements. Please try again.'}); } var record = await ResetToken.findOne({ where: { email: req.body.email, expiration: { [Op.gt]: Sequelize.fn('CURDATE')}, token: req.body.token, used: 0 } }); if (record == null) { return res.json({status: 'error', message: 'Token not found. Please try the reset password process again.'}); } var upd = await ResetToken.update({ used: 1 }, { where: { email: req.body.email } }); var newSalt = crypto.randomBytes(64).toString('hex'); var newPassword = crypto.pbkdf2Sync(req.body.password1, newSalt, 10000, 64, 'sha512').toString('base64'); await User.update({ password: newPassword, salt: newSalt }, { where: { email: req.body.email } }); return res.json({status: 'ok', message: 'Password reset. Please login with your new password.'});}); And again, the view: extends ../layout block content div.container div.row div.col h1 Reset password p Enter your new password below. if message div.reset-message.alert.alert-warning #{message} else div.reset-message.alert(style='display:none;') if showForm form#resetPasswordForm(onsubmit="return false;") div.form-group label(for="password1") New password: input.form-control#password1(type='password', name='password1') small.form-text.text-muted Password must be 8 characters or more. div.form-group label(for="password2") Confirm new password input.form-control#password2(type='password', name='password2') small.form-text.text-muted Both passwords must match. input#emailRp(type='hidden', name='email', value=record.email) input#tokenRp(type='hidden', name='token', value=record.token) div.form-group button#rpButton.btn.btn-success(type='submit') Reset password script. $('#rpButton').on('click', function() { $.post('/user/reset-password', { password1: $('#password1').val(), password2: $('#password2').val(), email: $('#emailRp').val(), token: $('#tokenRp').val() }, function(resp) { if (resp.status == 'ok') { $('.reset-message').removeClass('alert-danger').addClass('alert-success').show().text(resp.message); $('#resetPasswordForm').remove(); } else { $('.reset-message').removeClass('alert-success').addClass('alert-danger').show().text(resp.message); } }); });
This is what it should look like:
Your reset password form. (Large preview)
Add The Link To Your Login Page
Lastly, don’t forget to add a link to this flow from your login page! Once you do this, you should have a working reset password flow. Be sure to test thoroughly at each stage of the process to confirm everything works and your tokens have a short expiration and are marked with the correct status as the workflow progresses.
Next Steps
Hopefully this helped you on your way to coding a secure, user-friendly reset password feature.
If you are interested in learning more about cryptographic security, I recommend Wikipedia’s summary (warning, it’s dense!).
If you want to add even more security to your app’s authentication, look into 2FA. There are a lot of different options out there.
If I’ve scared you off from building your own reset password flow, you can rely on third-party login systems like Google and Facebook. PassportJS is a middleware you can use for NodeJS that implements these strategies.
(dm, yk, il)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
Via http://www.scpie.org/creating-secure-password-flows-with-nodejs-and-mysql/
source https://scpie.weebly.com/blog/creating-secure-password-flows-with-nodejs-and-mysql
0 notes
Text
Let’s Create Our Own Authentication API with Nodejs and GraphQL
Authentication is one of the most challenging tasks for developers just starting with GraphQL. There are a lot of technical considerations, including what ORM would be easy to set up, how to generate secure tokens and hash passwords, and even what HTTP library to use and how to use it.
In this article, we’ll focus on local authentication. It’s perhaps the most popular way of handling authentication in modern websites and does so by requesting the user’s email and password (as opposed to, say, using Google auth.)
Moreover, This article uses Apollo Server 2, JSON Web Tokens (JWT), and Sequelize ORM to build an authentication API with Node.
Handling authentication
As in, a log in system:
Authentication identifies or verifies a user.
Authorization is validating the routes (or parts of the app) the authenticated user can have access to.
The flow for implementing this is:
The user registers using password and email
The user’s credentials are stored in a database
The user is redirected to the login when registration is completed
The user is granted access to specific resources when authenticated
The user’s state is stored in any one of the browser storage mediums (e.g. localStorage, cookies, session) or JWT.
Pre-requisites
Before we dive into the implementation, here are a few things you’ll need to follow along.
Node 6 or higher
Yarn (recommended) or NPM
GraphQL Playground
Basic Knowledge of GraphQL and Node
…an inquisitive mind!
Dependencies
This is a big list, so let’s get into it:
Apollo Server: An open-source GraphQL server that is compatible with any kind of GraphQL client. We won’t be using Express for our server in this project. Instead, we will use the power of Apollo Server to expose our GraphQL API.
bcryptjs: We want to hash the user passwords in our database. That’s why we will use bcrypt. It relies on Web Crypto API‘s getRandomValues interface to obtain secure random numbers.
dotenv: We will use dotenv to load environment variables from our .env file.
jsonwebtoken: Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. jsonwebtokenwill be used to generate a JWT which will be used to authenticate users.
nodemon: A tool that helps develop Node-based applications by automatically restarting the node application when changes in the directory are detected. We don’t want to be closing and starting the server every time there’s a change in our code. Nodemon inspects changes every time in our app and automatically restarts the server.
mysql2: An SQL client for Node. We need it connect to our SQL server so we can run migrations.
sequelize: Sequelize is a promise-based Node ORM for Postgres, MySQL, MariaDB, SQLite and Microsoft SQL Server. We will use Sequelize to automatically generate our migrations and models.
sequelize cli: We will use Sequelize CLI to run Sequelize commands. Install it globally with yarn add --global sequelize-cli in the terminal.
Setup directory structure and dev environment
Let’s create a brand new project. Create a new folder and this inside of it:
yarn init -y
The -y flag indicates we are selecting yes to all the yarn init questions and using the defaults.
We should also put a package.json file in the folder, so let’s install the project dependencies:
yarn add apollo-server bcrpytjs dotenv jsonwebtoken nodemon sequelize sqlite3
Next, let’s add Babeto our development environment:
yarn add babel-cli babel-preset-env babel-preset-stage-0 --dev
Now, let’s configure Babel. Run touch .babelrc in the terminal. That creates and opens a Babel config file and, in it, we’ll add this:
{ "presets": ["env", "stage-0"] }
It would also be nice if our server starts up and migrates data as well. We can automate that by updating package.json with this:
"scripts": { "migrate": " sequelize db:migrate", "dev": "nodemon src/server --exec babel-node -e js", "start": "node src/server", "test": "echo \"Error: no test specified\" && exit 1" },
Here’s our package.json file in its entirety at this point:
{ "name": "graphql-auth", "version": "1.0.0", "main": "index.js", "scripts": { "migrate": " sequelize db:migrate", "dev": "nodemon src/server --exec babel-node -e js", "start": "node src/server", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "apollo-server": "^2.17.0", "bcryptjs": "^2.4.3", "dotenv": "^8.2.0", "jsonwebtoken": "^8.5.1", "nodemon": "^2.0.4", "sequelize": "^6.3.5", "sqlite3": "^5.0.0" }, "devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "^1.7.0", "babel-preset-stage-0": "^6.24.1" } }
Now that our development environment is set up, let’s turn to the database where we’ll be storing things.
Database setup
We will be using MySQL as our database and Sequelize ORM for our relationships. Run sequelize init (assuming you installed it globally earlier). The command should create three folders: /config /models and /migrations. At this point, our project directory structure is shaping up.
Let’s configure our database. First, create a .env file in the project root directory and paste this:
NODE_ENV=development DB_HOST=localhost DB_USERNAME= DB_PASSWORD= DB_NAME=
Then go to the /config folder we just created and rename the config.json file in there to config.js. Then, drop this code in there:
require('dotenv').config() const dbDetails = { username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, host: process.env.DB_HOST, dialect: 'mysql' } module.exports = { development: dbDetails, production: dbDetails }
Here we are reading the database details we set in our .env file. process.env is a global variable injected by Node and it’s used to represent the current state of the system environment.
Let’s update our database details with the appropriate data. Open the SQL database and create a table called graphql_auth. I use Laragon as my local server and phpmyadmin to manage database tables.
What ever you use, we’ll want to update the .env file with the latest information:
NODE_ENV=development DB_HOST=localhost DB_USERNAME=graphql_auth DB_PASSWORD= DB_NAME=<your_db_username_here>
Let’s configure Sequelize. Create a .sequelizerc file in the project’s root and paste this:
const path = require('path')
module.exports = { config: path.resolve('config', 'config.js') }
Now let’s integrate our config into the models. Go to the index.js in the /models folder and edit the config variable.
const config = require(__dirname + '/../../config/config.js')[env]
Finally, let’s write our model. For this project, we need a User model. Let’s use Sequelize to auto-generate the model. Here’s what we need to run in the terminal to set that up:
sequelize model:generate --name User --attributes username:string,email:string,password:string
Let’s edit the model that creates for us. Go to user.js in the /models folder and paste this:
'use strict'; module.exports = (sequelize, DataTypes) => { const User = sequelize.define('User', { username: { type: DataTypes.STRING, }, email: { type: DataTypes.STRING, }, password: { type: DataTypes.STRING, } }, {}); return User; };
Here, we created attributes and fields for username, email and password. Let’s run a migration to keep track of changes in our schema:
yarn migrate
Let’s now write the schema and resolvers.
Integrate schema and resolvers with the GraphQL server
In this section, we’ll define our schema, write resolver functions and expose them on our server.
The schema
In the src folder, create a new folder called /schema and create a file called schema.js. Paste in the following code:
const { gql } = require('apollo-server') const typeDefs = gql` type User { id: Int! username: String email: String! } type AuthPayload { token: String! user: User! } type Query { user(id: Int!): User allUsers: [User!]! me: User } type Mutation { registerUser(username: String, email: String!, password: String!): AuthPayload! login (email: String!, password: String!): AuthPayload! } ` module.exports = typeDefs
Here we’ve imported graphql-tag from apollo-server. Apollo Server requires wrapping our schema with gql.
The resolvers
In the src folder, create a new folder called /resolvers and create a file in it called resolver.js. Paste in the following code:
const bcrypt = require('bcryptjs') const jsonwebtoken = require('jsonwebtoken') const models = require('../models') require('dotenv').config() const resolvers = { Query: { async me(_, args, { user }) { if(!user) throw new Error('You are not authenticated') return await models.User.findByPk(user.id) }, async user(root, { id }, { user }) { try { if(!user) throw new Error('You are not authenticated!') return models.User.findByPk(id) } catch (error) { throw new Error(error.message) } }, async allUsers(root, args, { user }) { try { if (!user) throw new Error('You are not authenticated!') return models.User.findAll() } catch (error) { throw new Error(error.message) } } }, Mutation: { async registerUser(root, { username, email, password }) { try { const user = await models.User.create({ username, email, password: await bcrypt.hash(password, 10) }) const token = jsonwebtoken.sign( { id: user.id, email: user.email}, process.env.JWT_SECRET, { expiresIn: '1y' } ) return { token, id: user.id, username: user.username, email: user.email, message: "Authentication succesfull" } } catch (error) { throw new Error(error.message) } }, async login(_, { email, password }) { try { const user = await models.User.findOne({ where: { email }}) if (!user) { throw new Error('No user with that email') } const isValid = await bcrypt.compare(password, user.password) if (!isValid) { throw new Error('Incorrect password') } // return jwt const token = jsonwebtoken.sign( { id: user.id, email: user.email}, process.env.JWT_SECRET, { expiresIn: '1d'} ) return { token, user } } catch (error) { throw new Error(error.message) } } },
} module.exports = resolvers
That’s a lot of code, so let’s see what’s happening in there.
First we imported our models, bcrypt and jsonwebtoken, and then initialized our environmental variables.
Next are the resolver functions. In the query resolver, we have three functions (me, user and allUsers):
me query fetches the details of the currently loggedIn user. It accepts a user object as the context argument. The context is used to provide access to our database which is used to load the data for a user by the ID provided as an argument in the query.
user query fetches the details of a user based on their ID. It accepts id as the context argument and a user object.
alluser query returns the details of all the users.
user would be an object if the user state is loggedIn and it would be null, if the user is not. We would create this user in our mutations.
In the mutation resolver, we have two functions (registerUser and loginUser):
registerUser accepts the username, email and password of the user and creates a new row with these fields in our database. It’s important to note that we used the bcryptjs package to hash the users password with bcrypt.hash(password, 10). jsonwebtoken.sign synchronously signs the given payload into a JSON Web Token string (in this case the user id and email). Finally, registerUser returns the JWT string and user profile if successful and returns an error message if something goes wrong.
login accepts email and password , and checks if these details match with the one that was supplied. First, we check if the email value already exists somewhere in the user database.
models.User.findOne({ where: { email }}) if (!user) { throw new Error('No user with that email') }
Then, we use bcrypt’s bcrypt.compare method to check if the password matches.
const isValid = await bcrypt.compare(password, user.password) if (!isValid) { throw new Error('Incorrect password') }
Then, just like we did previously in registerUser, we use jsonwebtoken.sign to generate a JWT string. The login mutation returns the token and user object.
Now let’s add the JWT_SECRET to our .env file.
JWT_SECRET=somereallylongsecret
The server
Finally, the server! Create a server.js in the project’s root folder and paste this:
const { ApolloServer } = require('apollo-server') const jwt = require('jsonwebtoken') const typeDefs = require('./schema/schema') const resolvers = require('./resolvers/resolvers') require('dotenv').config() const { JWT_SECRET, PORT } = process.env const getUser = token => { try { if (token) { return jwt.verify(token, JWT_SECRET) } return null } catch (error) { return null } } const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => { const token = req.get('Authorization') || '' return { user: getUser(token.replace('Bearer', ''))} }, introspection: true, playground: true }) server.listen({ port: process.env.PORT || 4000 }).then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });
Here, we import the schema, resolvers and jwt, and initialize our environment variables. First, we verify the JWT token with verify. jwt.verify accepts the token and the JWT secret as parameters.
Next, we create our server with an ApolloServer instance that accepts typeDefs and resolvers.
We have a server! Let’s start it up by running yarn dev in the terminal.
Testing the API
Let’s now test the GraphQL API with GraphQL Playground. We should be able to register, login and view all users — including a single user — by ID.
We’ll start by opening up the GraphQL Playground app or just open localhost://4000 in the browser to access it.
Mutation for register user
mutation { registerUser(username: "Wizzy", email: "[email protected]", password: "wizzyekpot" ){ token } }
We should get something like this:
{ "data": { "registerUser": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzAwLCJleHAiOjE2MzA3OTc5MDB9.gmeynGR9Zwng8cIJR75Qrob9bovnRQT242n6vfBt5PY" } } }
Mutation for login
Let’s now log in with the user details we just created:
mutation { login(email:"[email protected]" password:"wizzyekpot"){ token } }
We should get something like this:
{ "data": { "login": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc" } } }
Awesome!
Query for a single user
For us to query a single user, we need to pass the user token as authorization header. Go to the HTTP Headers tab.
…and paste this:
{ "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc" }
Here’s the query:
query myself{ me { id email username } }
And we should get something like this:
{ "data": { "me": { "id": 15, "email": "[email protected]", "username": "Wizzy" } } }
Great! Let’s now get a user by ID:
query singleUser{ user(id:15){ id email username } }
And here’s the query to get all users:
{ allUsers{ id username email } }
Summary
Authentication is one of the toughest tasks when it comes to building websites that require it. GraphQL enabled us to build an entire Authentication API with just one endpoint. Sequelize ORM makes creating relationships with our SQL database so easy, we barely had to worry about our models. It’s also remarkable that we didn’t require a HTTP server library (like Express) and use Apollo GraphQL as middleware. Apollo Server 2, now enables us to create our own library-independent GraphQL servers!
Check out the source code for this tutorial on GitHub.
The post Let’s Create Our Own Authentication API with Nodejs and GraphQL appeared first on CSS-Tricks.
You can support CSS-Tricks by being an MVP Supporter.
Let’s Create Our Own Authentication API with Nodejs and GraphQL published first on https://deskbysnafu.tumblr.com/
0 notes